#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
#include "fs.h"

//********************** definitions *****************
#define TypeDir T_DIR
#define TypeFile T_FILE
#define TypeLink T_SYMLINK
#define TypeAll 100


#define BiggerThenSize 101
#define ExactlySize 102
#define SmallerThenSize 103
#define NoLimitSize 104

#define OptionNone 201
#define OptionHelp 202
#define OptionFollow 203
//***************************************************


//********************* options: **********************
  uint option = OptionNone;
  char* filename = 0;
  uint size = 0;
  uint sizeCmp = NoLimitSize;			
  short type = TypeAll;
//*****************************************************
  uint openFlag = O_RDONLY;
  

// ****************** functions declarations **********  
void   testFile(char* path);
void testDirFiles(int fd,char* path,struct stat* st);
char* fmtname(char *path);
//****************************************************

void printHelp()
{
  printf(1, "syntax:  find <path> <options> <preds> \n \
	  The path argument, which is the only mandatory argument, specifies the location where \n\
	  the search should begin and descend from. In case the provided path is a file and not a \n\
	  directory, then only the specified file will be tested for the specified criteria. The rest of \n\
	  the arguments are optional and are explained below. The output of find should be the \n\
	  full paths of all matches. \n\
	  Options: \n\
	  -follow \n\
	  Dereference symbolic links. If a symbolic link is encountered, apply tests to the \n\
	  target of the link. If a symbolic link points to a directory, then descend into it. \n\
	  -help \n\
	  Print a summary of the command-line usage of find and exit. \n\
	  Predicates: \n\
	  -name filename \n\
	  All files named (exactly, no wildcards) filename. \n\
	  -size (+/-)n \n\
	  File is of size n (exactly), +n (more than n), -n (less than n). \n\
	  -type c \n\
	  File is of type c: \n\
	  d   - directory \n\
	  f    - regular file \n\
	  s   - soft (symbolic) link \n\n\n");
}


char* fmtname(char *path)
{
  static char buf[DIRSIZ+1];
  char *p;
  
  // Find first character after last slash.
  for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;		// start from the end of path, and go back till u find a '/'
  p++;	// move 1 to get to filename start (not stay on '/')
  

  memmove(buf, p, strlen(p));
  // * adding this to cut the previous word in buffer
  buf[strlen(p)]=0;
  //* removing this because its added padded empty spaces at the and of the word:
  //memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
  return buf;
}


void testDirFiles(int fd,char* path,struct stat* st)
{
  char* buf, *p;
  struct dirent de;
  uint nameLength;
  
  buf = malloc(512);
  /*
    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf)
    {
      printf(1, "ls: path too long\n");
    }
    */
    
    strcpy(buf, path);
    p = buf+strlen(buf);		// p = pointer to end of path in buffer
    if(*(p-1) != '/')			// if the last char in path (in address p-1) is different then /
	*p++ = '/';				// add a slash to end of path, and move p to point to the end
    while(read(fd, &de, sizeof(de)) == sizeof(de))		// reading files entries inside directory
    {
      if(de.inum == 0)
        continue;
      if(strcmp(de.name,".")==0 || strcmp(de.name,"..")==0)
	continue;
      
      // add the name of the file that was found in the entry, to end of the path in the buffer:
      nameLength = strlen(de.name);
      memmove(p, de.name, nameLength);			//copy flie entry name to end of path (will go over the last file entry name copied there in the last iteration
      p[nameLength]=0; 						// mark end of path with 0, to not continue with info from previous iteration (previous file entry)
      
      // recusivly test the specific file entry:
      testFile(buf);
    }
    
    free(buf);
}



void testFile(char* path)
{
  int fd = open(path,openFlag);
  if(fd < 0)
  {
      printf(1,"file not found\n");
      return;
  }
  
  //printf(1,"DEBUG - fd = %d\n",fd);
  struct stat* fileStat = malloc(sizeof(struct stat));
  int isFileFound = fstat(fd,fileStat);
  if(isFileFound!=0)
  {
      printf(1,"file stat not found\n");
      return;
  }
  uint fileSize = fileStat->size;
  short fileType = fileStat->type;
  
  
  // if follow option, get real path into path (dont check if fileType symbolic, because after open the fd will already be the linked-to file!!!)
  if(option == OptionFollow)
  {
      char *realpath = malloc(512);
      readlink(path,realpath,512);
      path = realpath;
  }
  
  
  
  //printf(1,"DEBUG - fileSize = %d, fileType = %d\n",fileSize,fileType);
  // filter size:
  if(	(sizeCmp == NoLimitSize)						||
	(sizeCmp == BiggerThenSize && fileSize>size) 		||
	(sizeCmp == SmallerThenSize && fileSize<size)	 	||
	(sizeCmp == ExactlySize && fileSize==size) )
  {
	//printf(1,"DEBUG - size filter passed\n");
	// filter type:
	if(type == TypeAll 	||	 type == fileType)
	{ 
		 // printf(1,"DEBUG - type filter passed\n");
		  //filter filename:
		  char* fileFilename = fmtname(path);
		  //printf(1,"DEBUG - fileFilename:%s., filter filename:%s., compration: %d\n",fileFilename,filename,strcmp(fileFilename,filename) );
		  if(filename == 0 	||	 strcmp(fileFilename,filename) == 0 )		// no name filter, or filename equal filteredfilename
			  printf(1,"%s\n",path);
	}
  }
  
  
  // decend incase of directory file
  if(fileType == TypeDir)
  {
	testDirFiles(fd,path,fileStat);
  }
  close(fd);
}

  
int main(int argc, char *argv[])
{
  if(argc<2)		// dont have path
  {
    printf(1,"not enough parameters\n");
    exit();
  }
  char* rootPath = argv[1];
  //printf(1,"DEBUG - root path: %s, argc: %d\n", rootPath, argc);
  
  // parse the options:
  uint i;
  for(i=2;i<=argc;i++)
  {
    if(strcmp(argv[i],"-follow")==0)
    {
	option = OptionFollow;
    }
    else if(strcmp(argv[i],"-help")==0)
    {
	option = OptionHelp;
    }
    else if(strcmp(argv[i],"-name")==0)
    {
	filename = argv[i+1];
	i++;
    }
    else if(strcmp(argv[i],"-size")==0)
    {
	char* sizeStr = 0;
	char c = argv[i+1][0];
	if(c=='+')
	{
	  sizeCmp = BiggerThenSize;
	  sizeStr = &argv[i+1][1];			// string starting from index 1
	}
	else if(c=='-')
	{
	  sizeCmp = SmallerThenSize;
	  sizeStr = &argv[i+1][1];			// string starting from index 1
	}
	else
	{
	  sizeCmp = ExactlySize;
	  sizeStr = argv[i+1];			// string starting from index 1
	}
	size = atoi(sizeStr);
	i++;
    }
    else if(strcmp(argv[i],"-type")==0)
    {
	if(strcmp(argv[i+1],"d")==0)
	{
	  type = TypeDir;
	}
	else if(strcmp(argv[i+1],"f")==0)
	  type = TypeFile;
	else if(strcmp(argv[i+1],"s")==0)
	  type = TypeLink;
	else
	  type = TypeAll;
	i++;
    }
  }
  
 // printf(1,"DEBUG - option: %d , filename: %s, size: %d, sizeCmp: %d, type: %d\n", option,filename,size,sizeCmp, type );
    
  // handle help:
  if(option == OptionHelp)
  {
      printHelp();
      exit();
  }
  
  //handle follow:
  
  if(option == OptionFollow)
  {
      openFlag = O_RDONLY;
  }
  else
      openFlag = O_RDONLY  |  O_DONTREF;
  
  
     // start find
    testFile(rootPath);
  
  exit();
}

